﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using System.IO;
using WatiN.Core;
using WebAutoUtils;
using WebAutomation.Browsers;
using WebAutomation.Browsers.Chrome;
using WebAutomation.Browsers.FireFox;
using WebAutomation.Browsers.IE;
using BytescoutScreenCapturingLib;
using WebAutomation.Browsers.Safari;

namespace WebAutomation
{
    public class WebAuto
    {
        public class TestConfiguration
        {
            public string DefaultBrowser;
            public string DefaultStartURL;
            public string DefaultTestRepo;
            public string DefaultSQLConnection;
            public string DefaultEndOrFailURL;
            public int DefaultIterations;
            public string DefaultExcelFile;
            public bool DefaultRecordVideo;

        }

        public struct TestData
        {
           public string TestName;
           public string TestFileName;
           public string TestDescription;
           public string TestResultsFolder;
           public string TestBrowser;
           public string TestLogFolderLocation;

        }


        public static int TestId = 0;
        private string _globaErrorMessage = "No Errors";

        public Thread oThread;

        public delegate void OnTestRunComplete(Dictionary<TestData, Dictionary<int, List<TestLineResult>>> TestResults, Thread thread);
        public delegate void OnTestRunCompleteSimple(bool isPassed,string errorMessage);
        public delegate void OnTestRunStart(Dictionary<int, string> TestLines, Thread thread);
        public delegate void OnTestCommandStart(string[] parameters, int lineNumber, Thread thread, WebBrowserObject browserObject);
        public delegate void OnTestCommandCompleted(string[] parameters, int lineNumber, Thread thread, WebBrowserObject browserObject);


        private OnTestRunStart _onTestRunStart;
        private OnTestRunComplete _onTestRunComplete;
        private OnTestRunCompleteSimple _onTestRunCompleteSimple;
        private OnTestCommandStart _onTestCommandStart;
        private OnTestCommandCompleted _onTestCommandCompleted;

        private WebBrowserObject _browser;
        private WebBrowserObject _webBrowserObject;
        private WebAutoUtils.ITestDataExceptionRules _testDataExceptionRules;
        private string _testLines;

        public Dictionary<TestData, Dictionary<int, List<TestLineResult>>> TestResults =new Dictionary<TestData, Dictionary<int, List<TestLineResult>>>();
        public int? _noOfIterations = 1;
        public int _noOfTestFiles = 1;
        public Dictionary<int, string> TestLines = new Dictionary<int, string>();
        private bool _isDebugMode = false;
        public string TestLinesOrtestFilePath = "";
        public Browser BrowserObject = null;
        private ICapturer _capturer = null;
        private TestConfiguration _testConfiguration = null;
        private int _browserProcessId = 0;
        public WebAuto()
        {
            
        }
        public WebAuto(Browser browser = null, string testLinesOrtestFilePath="", TestConfiguration testConfiguration=null)
        {
            _testConfiguration = testConfiguration;
            if(browser!=null)
            {
                BrowserObject = browser;
            }
            TestLinesOrtestFilePath = testLinesOrtestFilePath;
            InitTest();

        }
        public WebAuto(string testLinesOrtestFilePath, TestConfiguration testConfiguration = null)
        {
            _testConfiguration = testConfiguration;
            TestLinesOrtestFilePath = testLinesOrtestFilePath;
            InitTest();
        }

        

        public ICommand TestCommand(string commandName)
        {
            return TestCommands.GetTestCommand(commandName);

        }

        public void InitTest()
        {
            if (TestLinesOrtestFilePath.Contains(",") && !TestLinesOrtestFilePath.Contains(">"))
            {
                string[] array = TestLinesOrtestFilePath.Split(',');
                int i = 0;
                foreach (var item in array)
                {
                    try
                    {
                        String line = "";
                        using (StreamReader sr = new StreamReader(item))
                        {
                            while ((line = sr.ReadLine()) != null)
                            {
                                _testLines += line + System.Environment.NewLine;
                            }

                            TestLines.Add(i, _testLines);
                            sr.Close();
                        }


                    }
                    catch
                    {

                    }
                    i++;
                }
            }
            else
            {

                //is it a directory
                bool isDirectory = Directory.Exists(TestLinesOrtestFilePath);
                if (isDirectory)
                {
                    var directories = Directory.GetFiles(TestLinesOrtestFilePath).ToList();
                    int i = 0;
                    foreach (var items in directories)
                    {
                        if (!TestLines.ContainsKey(i))
                        {
                            String line = "";
                            string tmpLine = "";
                            FileInfo fn = new FileInfo(items);
                            if (fn.Extension == ".txt")
                            {
                                using (StreamReader sr = new StreamReader(fn.FullName))
                                {
                                    while ((line = sr.ReadLine()) != null)
                                    {
                                        tmpLine += line + System.Environment.NewLine;
                                    }

                                    sr.Close();
                                }
                                TestLines.Add(i, tmpLine);
                            }
                        }
                        i++;
                    }
                }
                else
                {

                    if (File.Exists(TestLinesOrtestFilePath))
                    {
                        try
                        {
                            String line = "";
                            using (StreamReader sr = new StreamReader(TestLinesOrtestFilePath))
                            {
                                while ((line = sr.ReadLine()) != null)
                                {
                                    _testLines += line + System.Environment.NewLine;
                                }

                                sr.Close();
                            }


                        }
                        catch
                        {

                        }

                    }
                    else
                    {
                        _testLines = TestLinesOrtestFilePath;
                    }

                    TestLines.Add(1, _testLines);
                }
            }


        }

        public Thread RunTest(OnTestRunStart onTestRunStart, OnTestRunComplete onTestRunComplete, OnTestCommandStart onTestCommandStart, OnTestCommandCompleted onTestCommandCompleted, bool isDebugMode, WebBrowserObject webBrowserObject, WebAutoUtils.ITestDataExceptionRules testDataExceptionRules, OnTestRunCompleteSimple onTestRunCompleteSimple = null)
        {
            //if (!string.IsNullOrEmpty(onTestRunCompleteSimple))
            //{
            //    //OnTestRunCompleteSimple
            //    Type type = typeof (OnTestRunCompleteSimple);
            //    _onTestRunCompleteSimple = Delegate.CreateDelegate(type, type.GetMethod(onTestRunCompleteSimple)) as OnTestRunCompleteSimple;

            //}
            
            _onTestRunStart = onTestRunStart;
            _onTestRunComplete = onTestRunComplete;
            _onTestCommandStart = onTestCommandStart;
            _onTestCommandCompleted = onTestCommandCompleted;
            _onTestRunCompleteSimple = onTestRunCompleteSimple;
            
            _isDebugMode = isDebugMode;
            _webBrowserObject = webBrowserObject;
            if (testDataExceptionRules == null)
            {
                _testDataExceptionRules = General.GetExceptionRules();
            }
            else
            {
                _testDataExceptionRules = testDataExceptionRules;
            }
            oThread = new Thread(new ThreadStart(_runTest));
            oThread.SetApartmentState(ApartmentState.STA);
            oThread.Start();
            return oThread;
        }



        public bool RunTestNonThreaded(WebAutoUtils.ITestDataExceptionRules testDataExceptionRules, int processId = 0)
        {
            bool returnValue = false;
            try
            {

                if (testDataExceptionRules == null)
                {
                    _testDataExceptionRules = General.GetExceptionRules();
                }
                else
                {
                    _testDataExceptionRules = testDataExceptionRules;
                }
                _browserProcessId = processId;
                
                _runTest();
                
                returnValue = true;
            }catch(Exception ex)
            {
                //_globaErrorMessage = ex.InnerException.ToString();
            }

            return returnValue;
        }


        private void _runTest()
        {
            TestId++;
            string testName = "";
            string testFileName = "";
            string testDescription = "";
            string testResultsLocation = "";
            string browser = "";
            string url = "";
            string dbConnString = "";
            string testRepo = "";
            string failURL = "";
            string excelDataLocation = "";
            string ticks = DateTime.Now.Ticks.ToString();
            int noOfIterations = 1;
            bool recordScreen = false;
            string criticalError = "";
            TestLineResult testLineResult = new TestLineResult();
            string errorMessage = "";
            

            if (_onTestRunStart != null)
            {
                _onTestRunStart.Invoke(TestLines,oThread);
            }
            
            foreach (var items in TestLines)
            {

                string lines = items.Value;
                _testLines = lines;


                TestFileReader tfr = new TestFileReader(_testLines);
                noOfIterations = tfr.GetNoOfIterations(_testLines);
                tfr = null;

                if (noOfIterations == null)
                {
                    noOfIterations = 1;
                }
                if (noOfIterations == 0)
                {
                    noOfIterations = 1;
                }
                _noOfIterations = noOfIterations;

                TestFileReader.HasRemoveExcelDataBeenCalled = false;
                bool isTestStopped = false;
                for (int i = 0; i <= _noOfIterations - 1; i++)
                {
                    TestData testData = new TestData();
                    List<TestLineResult> listResult = new List<TestLineResult>();
                    try
                    {

                        TestFileReader testFileReader = new TestFileReader(_testLines);

                        testFileReader.ParseTestFile(ref testName, ref testFileName, ref testDescription,
                                                     ref testResultsLocation, ref browser, ref url, ref testRepo,
                                                     ref dbConnString, ref failURL, ref noOfIterations, null, ref excelDataLocation, ref recordScreen,ref criticalError);

                        if (!string.IsNullOrEmpty(criticalError))
                        {

                            throw new Exception(criticalError);
                        }
                        if (_testConfiguration!=null)
                        {

                            if (string.IsNullOrEmpty(browser))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultBrowser))
                                {
                                    browser = _testConfiguration.DefaultBrowser;
                                }
                            }
                            
                            if (string.IsNullOrEmpty(url))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultStartURL))
                                {
                                    url = _testConfiguration.DefaultStartURL;
                                }
                            }

                            if (string.IsNullOrEmpty(testRepo))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultTestRepo))
                                {
                                    testRepo = _testConfiguration.DefaultTestRepo;
                                }
                            }

                            if (string.IsNullOrEmpty(dbConnString))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultSQLConnection))
                                {
                                    dbConnString = _testConfiguration.DefaultSQLConnection;
                                }
                            }

                            if (string.IsNullOrEmpty(failURL))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultEndOrFailURL))
                                {
                                    failURL = _testConfiguration.DefaultEndOrFailURL;
                                }
                            }

                            if (noOfIterations == 0)
                            {
                                if (_testConfiguration.DefaultIterations > 0)
                                {
                                    noOfIterations = _testConfiguration.DefaultIterations;
                                }
                            }

                            if (string.IsNullOrEmpty(excelDataLocation))
                            {
                                if (!string.IsNullOrEmpty(_testConfiguration.DefaultExcelFile))
                                {
                                    excelDataLocation = _testConfiguration.DefaultExcelFile;
                                }
                            }
                            if (recordScreen == false)
                            {
                                recordScreen = _testConfiguration.DefaultRecordVideo;
                            }
                        }

                        if (string.IsNullOrEmpty(browser))
                        {
                            throw new Exception(
                                "The browser type has not been specified in either the test file or the config file.");
                        }

                        if (string.IsNullOrEmpty(url))
                        {
                            throw new Exception(
                                "The start url has not been specified in either the test file or the config file.");
                        }

                        if (string.IsNullOrEmpty(testRepo))
                        {
                            throw new Exception(
                                "The test repo location has not been specified in either the test file or the config file.");
                        }

                       

                        if (!_isDebugMode)
                        {
                            if (!Directory.Exists(testResultsLocation))
                            {
                                Directory.CreateDirectory(testResultsLocation);
                            }
                        }

                        testResultsLocation = testResultsLocation + "\\" + ticks + "_" + i;
                        if (!_isDebugMode)
                        {
                            if (!Directory.Exists(testResultsLocation))
                            {
                                Directory.CreateDirectory(testResultsLocation);
                            }
                        }
                        _browser = _getBrowserObject(browser, _webBrowserObject, _browserProcessId);
                        
                        //check exceptions 
                        if (!_testDataExceptionRules.IsDataConnectionAllowed(dbConnString))
                        {

                            throw new Exception("The Data connection (" + dbConnString + ") is not allowed.");
                        }
                        //check exceptions 
                        if (!_testDataExceptionRules.IsDataConnectionAllowed(url))
                        {

                            throw new Exception("The Start URL (" + url + ") is not allowed.");
                        }

                        //check exceptions 
                        if (!_testDataExceptionRules.IsEndOrFailURLAllowed(failURL))
                        {

                            throw new Exception("The End or Fail URL (" + url + ") is not allowed.");
                        }

                        //check exceptions 
                        if (!_testDataExceptionRules.IsExcelFileAllowed(excelDataLocation))
                        {

                            throw new Exception("The Excel file (" + excelDataLocation + ") is not allowed.");
                        }
                        if (!_isDebugMode)
                        {
                            if (recordScreen)
                            {

                                _startVideoRecording();

                            } 
                        }

                        testData.TestName = testName;
                        testData.TestFileName = testFileName;
                        testData.TestDescription = testDescription;
                        testData.TestResultsFolder = testResultsLocation;
                        testData.TestBrowser = browser;

                        if (!TestResults.ContainsKey(testData))
                        {
                            Dictionary<int, List<TestLineResult>> dictTmp = new Dictionary<int, List<TestLineResult>>();
                            dictTmp.Add(-1, new List<TestLineResult>());
                            TestResults.Add(testData, dictTmp);
                        }
                        
                       
                        int iLineCount = 0;
                        foreach (var item in testFileReader.TestCommandList)
                        {
                            if (!string.IsNullOrEmpty(item.CommandName))
                            {
                                
                                DateTime startTime = DateTime.Now;
                                TestCommands testCommands = new TestCommands(_browser,
                                                                             item.CommandName.Replace("\r\n", ""),
                                                                             item.CommandParameters, testData.TestFileName, testData.TestName);
                                bool stopTest = false;
                               
                                testLineResult = testCommands.ExecuteTestCommand(url, testRepo, testResultsLocation,
                                                                                 dbConnString, ref stopTest, _onTestCommandStart, _onTestCommandCompleted, oThread, iLineCount, _testDataExceptionRules);
                                testLineResult.StartTime = startTime;
                                testLineResult.EndTime = DateTime.Now;
                                testLineResult.LineNumber = iLineCount;
                                testLineResult.ResultsFolderLocation = testResultsLocation;
                                if (stopTest)
                                {
                                    testLineResult.LineMessage = "Test Stopped by command." + testLineResult.LineMessage;
                                }
                                listResult.Add(testLineResult);
                                
                                if (!testLineResult.LinePassed || stopTest||testLineResult.IsFrameworkError)
                                {
                                    _stopVideoRecording(testResultsLocation);
                                    isTestStopped = true;
                                    TestCommands.KillBrowserProcess(_browser.ProcessID, true);
                                    break;
                                }else
                                {
                                    isTestStopped = false;
                                }
                            }
                            iLineCount++;
                        }
                        TestResults[testData].Add(i, listResult);
                        errorMessage = "";
                        
                    }
                    catch (Exception ex)
                    {
                        TestFileReader.RemoveExcelData();
                        testLineResult.IsFrameworkError = true;
                        testLineResult.LinePassed = false;
                        testLineResult.LineMessage = "Critical error:" + ex.ToString();
                        try
                        {
                            listResult = new List<TestLineResult>();
                            TestLineResult errorTestLineResult = testLineResult;
                            listResult.Add(errorTestLineResult);
                            TestData td = new TestData();
                            td = TestFileReader.GetTestData(_testLines);
                            if (!TestResults.ContainsKey(td))
                            {
                                var dict = new Dictionary<int, List<TestLineResult>>();
                                dict.Add(i + 1, listResult);
                                TestResults.Add(td, dict);
                            }else
                            {
                                TestResults[td].Add(i + 1, listResult);    
                            }
                            
                        }catch{}
                        if (!string.IsNullOrEmpty(failURL))
                        {
                            TestCommands.GoToURLInBrowser(failURL);
                        }

                        _stopVideoRecording(testResultsLocation);
                        if (ex.InnerException != null)
                        {
                            errorMessage = ex.InnerException.ToString();
                        }else
                        {
                            errorMessage = ex.Message;
                        }
                        _globaErrorMessage = errorMessage;
                    }
                    if (!_isDebugMode)
                    {
                        _createTestResultsFile(testName, testFileName, testDescription, testResultsLocation, TestResults,
                                               i);
                    }


                    TestCommands._dataFromDB = new DataSet();
                    TestCommands._dataVariables = new Dictionary<string, DataTable>();
                    TestCommands.ListOfVariables = new Dictionary<string, string>();
                    if (isTestStopped)
                    {

                        break;
                    }
                    if (_noOfIterations > 1)
                    {
                        //wait for next iteration to run
                        Thread.Sleep(5000);
                    }
                   
                }

                if (_noOfIterations > 1)
                {
                    Thread.Sleep(5000);
                }
            }

            if (_onTestRunComplete != null)
            {
                _onTestRunComplete.Invoke(TestResults, oThread);

            }
            if (_onTestRunCompleteSimple!=null)
            {
                bool isPassed = string.IsNullOrEmpty(errorMessage);

                _onTestRunCompleteSimple.Invoke(isPassed, errorMessage);
            }

            testLineResult = new TestLineResult();
            TestResults = new Dictionary<TestData, Dictionary<int, List<TestLineResult>>>();
            _stopVideoRecording(testResultsLocation);


        }

        public string GetGlobaErrorMessage()
        {
            return _globaErrorMessage;

        }


        private void _startVideoRecording()
        {
            _capturer = new Capturer();
            _capturer.AudioEnabled = false;
            Screen[] screens = Screen.AllScreens;
            if (screens.Count() > 1)
            {


                _capturer.CapturingType = CaptureAreaType.catRegion;
                _capturer.CaptureRectTop = screens[1].Bounds.Top;
                _capturer.CaptureRectLeft = screens[1].Bounds.Left;
                _capturer.CaptureRectWidth = screens[1].Bounds.Width;
                _capturer.CaptureRectHeight = screens[1].Bounds.Height;

            }
            else
            {
                _capturer.CapturingType = CaptureAreaType.catScreen;

            }

            _capturer.OutputWidth = 1280;
            _capturer.OutputHeight = 960;
            _capturer.Run();


        }

        private void _stopVideoRecording(string saveFileFullLocation)
        {
            try
            {
                if (_capturer != null)
                {
                    saveFileFullLocation = saveFileFullLocation + @"\VideoRecording.avi";
                    _capturer.OutputFileName = saveFileFullLocation; // set output video filename to .WMV or .AVI file
                    _capturer.Stop();

                }
            }catch(Exception ex){}

        }

        private WebBrowserObject _getBrowserObject(string browser, WebBrowserObject webBrowserObject, int browserProcessId)
        {
            if (webBrowserObject != null)
            {
                return webBrowserObject;
            }
            else
            {
                if (browserProcessId == 0)
                {
                    TestCommands.KillBrowserProcess(false, browser);
                }
                switch (browser.ToLower())
                {
                    case "ie":
                        return new IEBrowser(browserProcessId);
                        break;
                    case "chrome":
                        return new ChromeBrowser(browserProcessId);
                        break;
                    case "safari":
                        throw new Exception("Safari is currently not supported in this version.");
                        break;
                    case "ff":
                    case "firefox":
                        //return new FireFoxBrowser();
                        throw new Exception("Fire fox is currently not supported in this version.");
                        break;
                    default:
                        return new IEBrowser(browserProcessId);
                        break;
                }
            }
            return null;

        }

        private void _createTestResultsFile(string testName, string testFileName, string testDescription, string testResultsLocation, Dictionary<TestData, Dictionary<int, List<TestLineResult>>> testResults, int countNumber)
        {
            StringBuilder sb = new StringBuilder();
            
            sb.AppendLine("---------------START OF TEST("+countNumber+")-----------------");
            sb.AppendLine("Test Results for:" + testName);
            sb.AppendLine("Date run:" + DateTime.Now);
            sb.AppendLine("Test Description:" + testDescription);
            sb.AppendLine("Commands:");
            int countOfFailed = 0;
            foreach(var item in testResults)
            {
                foreach(var items in item.Value)
                {
                    foreach (var itemsList in items.Value)
                    {
                        sb.AppendLine(itemsList.LineCommand.Replace("\r\n", "") + " : " +
                                      itemsList.LineParameter.Replace("\r\n", "") + " : " + " : " +
                                      itemsList.LineMessage.Replace("\r\n", "") + " : " + itemsList.LinePassed);
                        if (!itemsList.LinePassed)
                        {
                            countOfFailed++;
                        }
                    }
                }
            }

            sb.AppendLine(countOfFailed > 0 ? "Test FAILED" : "Test PASSED");
            sb.AppendLine("---------------END OF TEST-----------------");
            string testFileDirectory = testResultsLocation;
            if(countOfFailed>0)
            {

                try
                {
                    Directory.Move(testResultsLocation, testResultsLocation + "_Failed");
                }
                catch{}
                testFileDirectory = testResultsLocation + "_Failed";
                try
                {
                    Directory.Delete(testResultsLocation, true);
                }catch{}
            }
            string newFilename = testFileDirectory + "\\" + testFileName + "_" + DateTime.Now.Ticks + ".txt";

            System.IO.StreamWriter file = new System.IO.StreamWriter(newFilename);
            file.WriteLine(sb.ToString());

            file.Close();



        }


    }
}
